package com.crazy.web.config.shiro;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * shiro相关配置类
 *
 * @Author: zhaochaofeng
 * @Date: 2020-11-17
 * @Description:
 */
@Configuration
public class ShiroConfig {

    /**
     * 生成cookie管理器
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        rememberMeManager.setCipherKey(Base64.decode("0ooHuCiTA46LHOdJKHdLAg=="));
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        simpleCookie.setDomain("");
        simpleCookie.setPath("/");
        simpleCookie.setHttpOnly(true);
        //此处设定了浏览器关闭cookie清除
        simpleCookie.setMaxAge(-1);
        rememberMeManager.setCookie(simpleCookie);
        return rememberMeManager;
    }

    /**
     * 设置会话管理器
     * 当需要把项目部署到多台实例上时，此处会进行session同步
     * @return
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        SimpleCookie sessionIdCookie = new SimpleCookie("shiroweb");
        sessionIdCookie.setDomain("");
        sessionIdCookie.setPath("/");
        sessionIdCookie.setHttpOnly(true);
        //默认uid cookie 浏览器关闭后销毁
        sessionIdCookie.setMaxAge(-1);

        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        //session的过期时间，1小时
        sessionManager.setGlobalSessionTimeout(3600000);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionDAO(new EnterpriseCacheSessionDAO());
        sessionManager.setSessionIdCookieEnabled(true);
        sessionManager.setSessionIdCookie(sessionIdCookie);
        //相隔多久检查一次session的有效性
        sessionManager.setSessionValidationInterval(100000);
        sessionManager.setDeleteInvalidSessions(true);
        return sessionManager;
    }

    /**
     * 安全管理器
     * @return
     */
    @Bean
    public DefaultWebSecurityManager securityManager(@Autowired AccountRealm accountRealm, @Autowired CookieRememberMeManager cookieRememberMeManager,
                                                     @Autowired DefaultWebSessionManager defaultWebSessionManager) {
        // todo 给accountRealm的密码设置MD520次加密
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //对密码进行加密，MD5算法进行20次加密
        credentialsMatcher.setHashAlgorithmName("MD5");
        credentialsMatcher.setHashIterations(20);
        accountRealm.setCredentialsMatcher(credentialsMatcher);

        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(accountRealm);
        securityManager.setRememberMeManager(cookieRememberMeManager);
        securityManager.setCacheManager(new MemoryConstrainedCacheManager());
        securityManager.setSessionManager(defaultWebSessionManager);
        return securityManager;
    }

    /**
     * Filter工厂，设置对应的过滤条件和跳转条件
     *
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Autowired DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //登录
        shiroFilterFactoryBean.setLoginUrl("/login");
        //首页
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //错误页面，认证不通过跳转(只有perms，roles，ssl，rest，port才是属于AuthorizationFilter)
        shiroFilterFactoryBean.setUnauthorizedUrl("/error/403");
        /**
         * 3.配置shiro拦截器链
         * anon: 无需认证（登录）可以访问
         * authc: 必须认证才可以访问
         * user: 如果使用rememberMe的功能可以直接访问
         * perms： 该资源必须得到资源权限才可以访问
         * role: 该资源必须得到角色权限才可以访问
         *
         * 当应用开启了rememberMe时,用户下次访问时可以是一个user,但不会是authc,因为authc是需要重新认证的
         * 顺序从上到下,优先级依次降低
         * api开头的接口，走rest api鉴权，不走shiro鉴权
         */
        Map<String, String> filterChainDefinitions = new LinkedHashMap<>();
        filterChainDefinitions.put("/logout", "logout");
        filterChainDefinitions.put("/share/**", "user");
        filterChainDefinitions.put("/user/**", "user");
        filterChainDefinitions.put("/member/**", "user");
        filterChainDefinitions.put("/upload/**", "user");
        filterChainDefinitions.put("/outer/**", "anon");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitions);
        return shiroFilterFactoryBean;
    }

    /**
     * 在方法中 注入  securityManager ，进行代理控制
     * @return
     */
    @Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean(@Autowired DefaultWebSecurityManager securityManager) {
        MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
        methodInvokingFactoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        methodInvokingFactoryBean.setArguments(securityManager);
        return methodInvokingFactoryBean;
    }

    /**
     * 加入注解的使用，不加入这个注解不生效
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * Shiro生命周期处理器:
     * 用于在实现了Initializable接口的Shiro bean初始化时调用Initializable接口回调(例如:CustomRealm)
     * 在实现了Destroyable接口的Shiro bean销毁时调用 Destroyable接口回调(例如:DefaultSecurityManager)
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public static DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }

}
